<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <title>Prex Driver-Kernel Interface</title>
  <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
  <meta name="keywords" content="Prex, embedded, real-time, operating system, RTOS, open source, free">
  <meta name="author" content="Kohsuke Ohtani">
  <link rel="stylesheet" type="text/css" href="../default.css" media="screen">
  <link rel="stylesheet" type="text/css" href="../print.css" media="print">
</head>
<body>
<div id="top">
</div>
<div id="middle">

<table id="content" cellpadding="0" cellspacing="0">
  <tbody>

    <tr>
      <td id="header" colspan="2" valign="top">
        <table width="100%" border="0" cellspacing="0" cellpadding="0">
        <tr>
          <td id="logo">
            <a href="http://prex.sourceforge.net/">
            <img alt="Prex logo" src="../img/logo.gif" border="0"
            style="width: 250px; height: 54px;"></a>
          </td>
          <td id="brief" align="right" valign="bottom">
            An Open Source, Royalty-free,<br>
	    Real-time Operating System
          </td>
        </tr>
        </table>
      </td>
    </tr>

    <tr>
      <td id="directory" style="vertical-align: top;">
      <a href="http://prex.sourceforge.net/">Prex Home</a> >
      <a href="index.html">Document Index</a> >
      Driver-Kernel Interface
    </tr>
    <tr><td class="pad" colspan="2" style="vertical-align: top;"></td></tr>

    <tr>
      <td id="doc" style="vertical-align: top;">
      <h1>Prex Driver-Kernel Interface</h1>

<i>Version 1.0.3, 2008/10/13</i><br>
<br>

<h3>Table of Contents</h3>
<ul>
  <li><a href="#intro">Introduction</a></li>
</ul>
<ul>
  <li><a href="#gen">General Information</a>
  <ul>
    <li><a href="#gen">Header File</a></li>
    <li><a href="#gen">Data Types</a></li>
    <li><a href="#gen">Calls from ISR</a></li>
  </ul>
  </li>
</ul>
<ul>
  <li><a href="#obj">Device Object</a>
  <ul>
    <li><a href="#obj">device_create</a></li>
    <li><a href="#obj">device_destroy</a></li>
    <li><a href="#obj">device_broadcast</a></li>
  </ul>
  </li>
</ul>
<ul>
  <li><a href="#kmem">Kernel Memory</a>
  <ul>
    <li><a href="#kmem">kmem_alloc</a></li>
    <li><a href="#kmem">kmem_free</a></li>
    <li><a href="#kmem">kmem_map</a></li>
  </ul>
  </li>
</ul>
<ul>
  <li><a href="#umem">User Memory</a>
  <ul>
    <li><a href="#umem">umem_copyin</a></li>
    <li><a href="#umem">umem_copyout</a></li>
    <li><a href="#umem">umem_strnlen</a></li>
  </ul>
  </li>
</ul>
<ul>
  <li><a href="#page">Physical Page</a>
  <ul>
    <li><a href="#page">page_alloc</a></li>
    <li><a href="#page">page_free</a></li>
    <li><a href="#page">page_reserve</a></li>
  </ul>
  </li>
</ul>
<ul>
  <li><a href="#int">Interrupt</a>
  <ul>
    <li><a href="#int">irq_attach</a></li>
    <li><a href="#int">irq_detach</a></li>
    <li><a href="#int">irq_lock</a></li>
    <li><a href="#int">irq_unlock</a></li>
  </ul>
  </li>
</ul>
<ul>
  <li><a href="#sched">Scheduler</a>
  <ul>
    <li><a href="#sched">sched_lock</a></li>
    <li><a href="#sched">sched_unlock</a></li>
    <li><a href="#sched">sched_tsleep</a></li>
    <li><a href="#sched">sched_wakeup</a></li>
    <li><a href="#sched">sched_dpc</a></li>
  </ul>
  </li>
</ul>
<ul>
  <li><a href="#timer">Timer</a>
  <ul>
    <li><a href="#timer">timer_callout</a></li>
    <li><a href="#timer">timer_stop</a></li>
    <li><a href="#timer">timer_delay</a></li>
    <li><a href="#timer">timer_count</a></li>
    <li><a href="#timer">timer_hook</a></li>
  </ul>
  </li>
</ul>
<ul>
  <li><a href="#misc">Miscellaneous</a>
  <ul>
    <li><a href="#misc">exception_post</a></li>
    <li><a href="#misc">machine_bootinfo</a></li>
    <li><a href="#misc">machine_reset</a></li>
    <li><a href="#misc">machine_setpower</a></li>
    <li><a href="#misc">machine_halt</a></li>
    <li><a href="#misc">phys_to_virt</a></li>
    <li><a href="#misc">virt_to_phys</a></li>
    <li><a href="#misc">debug_attach</a></li>
    <li><a href="#misc">debug_dump</a></li>
    <li><a href="#misc">printf</a></li>
    <li><a href="#misc">panic</a></li>
  </ul>
  </li>
</ul>
<br>
<br>

<h2 id="intro">Introduction</h2>
<p>
The Prex kernel provides the minimum service for the device drivers.
Since the driver module is separated from the kernel module, the drivers
can not access other kernel functions beyond this interface.
This mechanism helps to isolate the kernel from the driver codes.
</p>
<p>
This document describes the Driver-Kernel Interface (DKI) that can be
used by device drivers.
</p>

<h2 id="gen">General Information</h2>


<h3 id="hf">Header File</h3>
<p>
The Prex driver header file provides
common driver services in the kernel. A device driver must
include this header file to use the driver-kernel interface.
</p>
<pre>
#include &lt;driver.h&gt;
</pre>


<h3>Data Types</h3>
<p>
The following data types are defined by kernel.
</p>

<table border="1" width="60%" cellspacing="0">
<tbody>
<tr>
  <th>Data type</th>
  <th>Description</th>
</tr>

<tr>
  <td>device_t</td>
  <td>Used to identify the device object.</td>
</tr>
<tr>
  <td>task_t</td>
  <td>Used to identify the task.</td>
</tr>
</tbody>
</table>

<h3>Calls from ISR</h3>
<p>
The driver-kernel service is limited at interrupt level
because the kernel does not synchronize all data accesses
for interrupt level access.
So, the device driver can use only the following functions
from the interrupt service routine.
</p>
<ul>
  <li>irq_lock()</li>
  <li>irq_unlock()</li>
  <li>sched_wakeup()</li>
  <li>sched_stat()</li>
  <li>timer_callout()</li>
  <li>timer_stop()</li>
  <li>timer_count()</li>
  <li>sched_lock()</li>
  <li>sched_unlock()</li>
  <li>sched_tsleep()</li>
  <li>sched_wakeup()</li>
  <li>sched_dpc()</li>
  <li>exception_post()</li>
  <li>printf()</li>
  <li>panic()</li>
  <li>machine_reset()</li>
  <li>machine_dump()</li>
</ul>


<h2 id="obj">Device Object</h2>
<p>
The device object is created by the driver to communicate to the
application. Usually, the driver creates a device object for an existing
physical device. And, it can also be used to handle logical or virtual devices.
</p>

<pre>
device_t device_create(const struct devio *io, const char *name, int flags);
int device_destroy(device_t dev);
int device_broadcast(int event, int force);
</pre>

<dl>
<dt>device_create()</dt>
<dd>
Creates device object with the specified name in <i>name</i>.
The <i>io</i> argument points to the device I/O table for the device object.
This function returns the ID of the created device object on success, or
0 on failure.
<p>
All device object has its associated device I/O table. The table defines
the function pointer for the corresponding request routines.
The definition of device I/O table is as follows:
</p>
<pre>
struct devio {
        int     (*open)(device_t dev, int mode);
        int     (*close)(device_t dev);
        int     (*read)(device_t dev, char *buf, u_long *size, u_long offset);
        int     (*write)(device_t dev, char *buf, u_long *size, u_long offset);
        int     (*ioctl)(device_t dev, int cmd, u_long arg);
        int     (*event)(device_t dev, int event);
};
</pre>
The driver can specify the one same I/O table for the different device
objects. All device I/O routine can get the device object ID as its
first argument. So, each routine can determine the request is sent to
which device object.
</dd>
</dl>

<dl>
<dt>device_destroy()</dt>
<dd>
Deletes the device object specified in <i>dev</i>.
This function returns ENODEV if the specified device object does not exist.
</dd>
</dl>

<dl>
<dt>device_broadcast()</dt>
<dd>
Broadcasts the message specified by <i>event</i> to all device objects.
If <i>force</i> is true, a kernel will ignore the value returned by
each driver, and continue event notification.
If <i>force</i> is false and any driver returns any error for the event,
a kernel stops the event notification. In this case, this function
returns an error code which is returned by that driver.
</dd>
</dl>


<h2 id="kmem">Kernel Memory</h2>
The kernel provides the following memory allocation services for drivers.
Please note that it can not allocate lager buffer than one page.
If the driver needs larger buffer, it should use page_alloc()
 instead of kmem_alloc().
<pre>
void *kmem_alloc(size_t size);
void  kmem_free(void *ptr);
void *kmem_map(void *addr, size_t size);
</pre>

<dl>
<dt>kmem_alloc()</dt>
<dd>
Allocates the kernel buffer for the specified <i>size</i> bytes.
It returns the pointer to the allocated buffer on success, or NULL on failure.
</dd>
</dl>

<dl>
<dt>kmem_free()</dt>
<dd>
Frees the allocated kernel buffer pointed by <i>ptr</i>.
</dd>
</dl>

<dl>
<dt>kmem_map()</dt>
<dd>
Maps the specified virtual address <i>addr</i> to the kernel address.
It returns the pointer mapped in the kernel memory on success, or NULL if
there is no mapped memory.
</dd>
</dl>

<h2 id="umem">User Memory</h2>
<p>
Since an access to user memory may cause a page fault, the user
buffer manipulation is handled by the kernel core code.
The driver should not access the user buffer directly. Instead, 
it should use the following kernel services.
</p>
<pre>
int umem_copyin(void *uaddr, void *kaddr, size_t len);
int umem_copyout(void *kaddr, void *uaddr, size_t len);
int umem_strnlen(const char *uaddr, size_t maxlen, size_t *len);
</pre>

<dl>
<dt>umem_copyin()</dt>
<dd>
Copies the data from the user buffer to the kernel area.
Returns 0 on success, or EFAULT on failure.
</dd>
</dl>

<dl>
<dt>umem_copyout()</dt>
<dd>
Copies the data from the kernel buffer to the user area.
Returns 0 on success, or EFAULT on failure.
</dd>
</dl>

<dl>
<dt>umem_strnlen()</dt>
<dd>
Gets the length of specified string in <i>uaddr</i>.
Returns 0 on success, or EFAULT on failure.
The returned length does not include a NULL terminator.
</dd>
</dl>

<h2 id="page">Physical Page</h2>
<pre>
void *page_alloc(u_long size);
void  page_free(void *addr, u_long size);
int   page_reserve(void *addr, u_long size);
</pre>

<dl>
<dt>page_alloc()</dt>
<dd>
Allocates continuous pages for the specified <i>size</i> bytes.
This function returns the physical address of the allocated pages, or
returns NULL on failure. The kernel does not zero-fill this new page.
The requested size is automatically round up to the page boundary.
</dd>
</dl>

<dl>
<dt>page_free()</dt>
<dd>
Frees allocated page block. The caller must provide the size information
in <i>size</i> argument that was specified for page_alloc().
</dd>
</dl>

<dl>
<dt>page_reserve()</dt>
<dd>
Reserves pages in the specified address.
This function returns 0 on success, or -1 on failure.
</dd>
</dl>


<h2 id="int">Interrupt</h2>
<p>
The Prex kernel encapsulates the save/restore of the previous interrupt
state. irq_lock() will automatically save the previous interrupt state
if needed. So, the driver must call irq_unlock() for the same count of
the irq_lock() call.
</p>

<pre>
int  irq_attach(int irqno, int prio, int shared, int (*isr)(int), void (*ist)(int));
void irq_detach(int handle);
void irq_lock(void);
void irq_unlock(void);
</pre>

<dl>
<dt>irq_attach()</dt>
<dd>
Attaches to the <i>ISR</i> (interrupt service request) and <i>ist</i>
(interrupt service thread) to the interrupt vector specified in <i>irqno</i>.
The argument <i>prio</i> is the logical interrupt priority level. The
priority value 0 is lowest priority for interrupt processing.
The caller can specify one of the following interrupt priority levels.
<pre>
#define IPL_NONE        0       /* Nothing */
#define IPL_COMM        1       /* Serial, Parallel */
#define IPL_BLOCK       2       /* FDD, IDE */
#define IPL_NET         3       /* Network */
#define IPL_DISPLAY     4       /* Screen */
#define IPL_INPUT       5       /* Keyboard, Mouse */
#define IPL_AUDIO       6       /* Audio */
#define IPL_BUS         7       /* USB, PCCARD */
#define IPL_RTC         8       /* RTC Alarm */
#define IPL_PROFILE     9       /* Profiling timer */
#define IPL_CLOCK       10      /* System Clock Timer */
#define IPL_HIGH        11      /* Everything */
</pre>
If <i>shared</i> argument is true, the kernel allows the other irq owner
to attach to the same irq vector.
</dd>
</dl>

<dl>
<dt>irq_detach()</dt>
<dd>
Detaches the interrupt from the IRQ specified by <i>handle</i>.
</dd>
</dl>

<dl>
<dt>irq_lock()</dt>
<dd>
Masks all H/W interrupts, and increments the IRQ lock count.
</dd>
</dl>

<dl>
<dt>irq_unlock()</dt>
<dd>
Decrements the IRQ lock count. If the IRQ lock count becomes 0, 
the H/W interrupts are unmasked.
</dd>
</dl>


<h2 id="sched">Scheduler</h2>
<p>
The thread can sleep/wakeup for the specific event. The event works as
the queue of the sleeping threads.
</p>

<pre>
void sched_lock(void);
void sched_unlock(void);
int  sched_tsleep(struct event *evt, u_long timeout);
void sched_wakeup(struct event *evt);
void sched_dpc(struct dpc *dpc, void (*func)(void *), void *arg);
</pre>

<dl>
<dt>sched_lock()</dt>
<dd>
Disables the thread switch, and increments the scheduling lock count.
This is used to synchronize the thread execution to protect 
global resources. Since the scheduling lock count can be nested, 
the caller must call the sched_unlock() routine the same number of
lock count.
</dd>
</dl>

<dl>
<dt>sched_unlock()</dt>
<dd>
Decrements the scheduling lock count. If the scheduling lock count becomes 0, 
the thread switch is enabled again.
</dd>
</dl>

<dl>
<dt>sched_tsleep()</dt>
<dd>
Sleep the current thread until specified event occurs.
The caller can specify <i>timeout</i> value in msec.
If the <i>timeout</i> value is 0, the timeout timer does not work.
<p>
The definition of the event for sleep/wakeup is as follows:
</p>
<pre>
struct event {
        struct queue    sleepq;         /* Queue for waiting thread */
        char            *name;          /* Event name */
};
</pre>
</dd>
</dl>

<dl>
<dt>sched_wakeup()</dt>
<dd>
Wakes up all threads that are waiting for the specified event.
</dd>
</dl>

<dl>
<dt>sched_dpc()</dt>
<dd>
Programs DPC (Deferred Procedure Call).
<p>
The definition of the DPC object is as follows:
</p>
<pre>
struct dpc {
        struct queue    link;           /* Linkage on DPC queue */
        int             state;
        void            (*func)(void *); /* Call back routine */
        void            *arg;           /* Argument to pass */
};
</pre>
</dd>
</dl>

<h2 id="timer">Timer</h2>
<pre>
void   timer_callout(struct timer *tmr, void (*func)(u_long), u_long arg, u_long msec);
void   timer_stop(struct timer *tmr);
u_long timer_delay(u_long msec);
u_long timer_count(void);
int    timer_hook(void (*func)(int));
</pre>


<dl>
<dt>timer_callout()</dt>
<dd>
Requests a call out timer. The specified <i>func</i> routine will be
called with <i>arg</i> argument after <i>msec</i>. The caller must
allocate the memory for the timer structure for <i>tmr</i>.
<p>
The definition of the timer structure is as follows:
</p>
<pre>
struct timer {
        struct list link;           /* Linkage on timer chain */
        int         active;         /* True if active */
        u_long      expire;         /* Expire time (ticks) */
        u_long      interval;       /* Time interval */
        void (*func)(void *);       /* Function to call */
        void        *arg;           /* Function argument */
        struct event event;         /* Event for this timer */
};
</pre>
</dd>
</dl>

<dl>
<dt>timer_stop()</dt>
<dd>
Stops a running timer.
</dd>
</dl>

<dl>
<dt>timer_delay()</dt>
<dd>
Delays thread execution.
</dd>
</dl>


<dl>
<dt>timer_count()</dt>
<dd>
Returns current timer count (ticks since bootup).
</dd>
</dl>

<dl>
<dt>timer_hook()</dt>
<dd>
Installs a hook routine for the timer tick.
</dd>
</dl>


<h2 id="misc">Miscellaneous</h2>
<pre>
int   exception_post(task_t task, int exc);
void  machine_bootinfo(struct bootinfo **pbi);
void  machine_reset(void);
void  machine_halt(void);
void  machine_setpower(int);
void *phys_to_virt(void *p_addr);
void *virt_to_phys(void *v_addr);
void  debug_dump(int index);
void  debug_attach(void (*func)(char *));
void  printf(const char *fmt, ...);
void  panic(const char *fmt, ...);
</pre>

<dl>
<dt>exception_post()</dt>
<dd>
Posts an exception for the specific task.
</dd>
</dl>

<dl>
<dt>machine_bootinfo()</dt>
<dd>
Returns the pointer to the system boot infomation structure.
<p>
 The format of the boot information is as follows:
</p>
<pre>
struct bootinfo
{
        struct vidinfo  video;
        struct physmem  ram[NMEMS];     /* physical ram table */
        int             nr_rams;        /* number of ram blocks */
        struct physmem  bootdisk;       /* boot disk in memory */
        int             nr_tasks;       /* number of boot tasks */
        struct module   kernel;         /* kernel image */
        struct module   driver;         /* driver image */
        struct module   tasks[1];       /* boot tasks image */
};
</pre>

</dd>
</dl>

<dl>
<dt>machine_reset()</dt>
<dd>
Resets the system.
</dd>
</dl>

<dl>
<dt>machine_halt()</dt>
<dd>
Halts the system.
</dd>
</dl>

<dl>
<dt>machine_setpower()</dt>
<dd>
Set the system power.
</dd>
</dl>

<dl>
<dt>phys_to_virt()</dt>
<dd>
Return the kernel virtual address from specifid physical address.
</dd>
</dl>

<dl>
<dt>virt_to_phys()</dt>
<dd>
Return the mapped physical address from specifid virtual address.
</dd>
</dl>

<dl>
<dt>debug_dump()</dt>
<dd>
Dumps the system information for debug.
</dd>
</dl>

<dl>
<dt>debug_attach()</dt>
<dd>
Attaches to the external output routine for the printf().
</dd>
</dl>

<dl>
<dt>printf()</dt>
<dd>
Prints the driver message to the output device.
The message is enabled only with debugging kernel.
</dd>
</dl>

<dl>
<dt>panic()</dt>
<dd>
Stops the system for the fatal error.
</dd>
</dl>


      </td>
    </tr>
    <tr>
      <td id="footer" colspan="2" style="vertical-align: top;">
        <a href="http://sourceforge.net">
        <img src="http://sourceforge.net/sflogo.php?group_id=132028&amp;type=1"
        alt="SourceForge.net Logo" border="0" height="31" width="88"></a><br>
        Copyright&copy; 2005-2007 Kohsuke Ohtani
      </td>
    </tr>

  </tbody>
</table>

</div>
<div id="bottom"></div>

</body>
</html>
